1. Field of the Invention
Embodiments of the present invention relate to the field of class loading in a Java Virtual Machine environment. More particularly, embodiments of the present invention relate generally to automated root cause analysis of class loading failures in the Java Virtual Machine environment.
2. Related Art
Java programs are composed of named classes (e.g., “java.lang.String”) and other data is referred to as resource data (e.g., “resource”). Classes and resources are loaded into a Java Virtual Machine (JVM) by a class loader. The term “class loading” refers to the process of locating the bytes for a given class name and converting them into a Java class instance. All “java.lang.Class” instances within a JVM start life as an array of bytes structured in the class format defined by the JVM specification.
Class loading is performed by the JVM during the startup process, and subsequently by subclasses of the “java.lang.ClassLoader” class. These “classloaders” provide an abstraction that allows the JVM to load classes without any knowledge of where the class bytes come from. This enables both local and remote storage as well as dynamic class generation. Furthermore, class loaders provide “dynamic-loading” capabilities that provide a great deal of extensibility to the Java language and are the basis for the mobile code capabilities of the Java language. In addition to loading classes, classloaders can be used to load native code (e.g., .dll files).
A classloader is a subclass of the “java.lang.ClassLoader” class that is responsible for loading classes. In a JVM environment class loaders are arranged in a tree hierarchy, where each class loader has a single parent. Each class loader is designed to work with one or more code-sources. For example, each classloader contains one or more code-sources that are class or resource archives (e.g., jars, zip files, etc.) from which to obtain class files to convert into class instances or resource data for application use.
A code-source is a root location from which the classloader searches for classes. Although the name implies the loading of classes only, class loaders can be used to load any file from the configured code-sources using, for example, the “getResource” and “getResrouceAsStream” methods. Code-sources can be defined to represent physical storage of binary class files, java sources that must first be compiled, or even classes generated on the fly. For example, a code-source can be a directory, a zip file, a jar file, etc.
In a Java application, there may be a number of different classloaders that use any number of different mechanisms to load classes. As an example, classloaders can be designed such that classes are retrieved from the following: a database where the configuration consists of all the data needed to point at the correct table(s) in a specific database; a remote server running a proprietary communications protocol, where the configuration consists of DNS names, ports, and other network information; a file system including a special search order specified in a properties file; sources defined within an XML file; source code (e.g., *.java) files that must be compiled, etc.
When a class load event occurs, a class loader is selected (e.g., the “initiating” classloader) to search for the class. In a standard search algorithm, the classloader searches up the parent chain in the classloader tree of the JVM environment before searching for the class in the initiating classloader. The classloader that locates and converts the class file into a class instance is referred to as the “defining” loader.
Java classes may have dependencies on other classes. The JVM may resolve these dependencies during the loading of the dependent class. On the other hand, in the interests of speed and efficiency, the JVM may defer resolution until the dependency is first required during a method execution, referred to as “lazy loading.”
When a class loading error occurs, such as a required class being missing, an exception is thrown. These exceptions often cause program termination since the required class cannot be loaded. Determining the cause of such an error is often extremely difficult, particularly in environments with multiple class loaders, such as the J2EE environment. For example, the exceptions usually contain a message describing the problem. However, the message contains very little information (e.g., usually only identifying the unloadable class). This is especially true when dealing with dependent classes and their visibility.
In addition, in a JVM environment, the set of classloaders and their configurations are large, complex, and opaque to the user. Additionally, most of the code pathways are either non-debug or in native code. Also, classloading can be highly recursive, adding to the complexity in determining the sequence of events leading to the unloadable class. In addition, the exception may not be thrown when the class is loaded. Instead, the exception is thrown much later when executing the class. As a result, determining the sequence of events leading to the unloadable class to discover the source of the problem would be difficult.